﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Jermaine.UI
{
    public class UnhandledExceptionHandler
    {
        private UnhandledExceptionHandler() { }

        public static event EventHandler<ExceptionHandlingEventArgs> HandlingException;

        private static ApplicationContext context;

        /// <summary>
        /// Handle App domain unhandled exception
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void AppDomainUnhandledException(object sender, UnhandledExceptionEventArgs e) {
            var exception = e.ExceptionObject as Exception;
            OnHandleException(exception);

        }

        private static void OnHandleException(Exception exception) {
            if (HandlingException != null) {
                HandlingException(null, new ExceptionHandlingEventArgs {
                    Message = ExtractStackTrace(exception),
                    ExceptionObject = exception
                });
            } else {
                HandleException(exception);
            }
        }

        /// <summary>
        /// Handle exception
        /// </summary>
        /// <param name="exception"></param>
        private static void HandleException(Exception exception) {
            var title = System.Diagnostics.Process.GetCurrentProcess().ProcessName;
            var message = ExtractStackTrace(exception);
            var result = MessageBox.Show(context.MainForm, message, title, MessageBoxButtons.OK, MessageBoxIcon.Error);
            if (result == DialogResult.OK) {
                Form form = null;
                if (context != null) {
                    form = context.MainForm;
                }

                try {
                    if (form != null) {
                        form.Close();
                    } else {
                        Environment.Exit(0);
                    }
                } catch {
                    Environment.Exit(-1);
                }

            }
        }


        /// <summary>
        /// Main entry,listen the app domain,deal the unhandled exceptions
        /// </summary>
        /// <param name="context"></param>
        /// <param name="appDomain"></param>
        public static void Listen(AppDomain appDomain, ApplicationContext context = null) {
            UnhandledExceptionHandler.context = context;
            appDomain.UnhandledException += AppDomainUnhandledException;
            Application.ThreadException += ApplicationThreadException;
        }

        /// <summary>
        /// Handle Application thread exception
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void ApplicationThreadException(object sender, System.Threading.ThreadExceptionEventArgs e) {
            OnHandleException(e.Exception);
        }

        /// <summary>
        /// extract stack trace
        /// </summary>
        /// <param name="exception"></param>
        /// <returns></returns>
        private static string ExtractStackTrace(Exception exception) {
            var stackBuilder = new StringBuilder();

            stackBuilder.AppendLine(String.Format("Time:{0}", DateTime.Now.ToString()));
            stackBuilder.AppendLine(String.Format("Source:{0}", exception.Source));
            if (exception.TargetSite != null) {
                stackBuilder.AppendLine(String.Format("Target:{0}", exception.TargetSite));
            }

            stackBuilder.AppendLine(String.Format("Type:{0} ", exception.GetType()));
            stackBuilder.AppendLine(String.Format("Message:{0} ", exception.Message));
            stackBuilder.AppendLine(String.Format("Stack:{0}", exception.StackTrace));

            if (exception.InnerException != null) {
                var inner = exception.InnerException;

                while (inner != null) {
                    stackBuilder.AppendLine();
                    stackBuilder.AppendLine("------Inner Exeception------");
                    stackBuilder.AppendLine();
                    stackBuilder.AppendLine(String.Format("Source: {0}", inner.Source));
                    if (exception.TargetSite != null) {
                        stackBuilder.AppendLine(String.Format("Target:{0}", inner.TargetSite));
                    }
                    stackBuilder.AppendLine(String.Format("Type:{0} ", inner.GetType()));
                    stackBuilder.AppendLine(String.Format("Message:{0} ", inner.Message));
                    stackBuilder.AppendLine(String.Format("Stack:{0}", inner.StackTrace));

                    inner = inner.InnerException;
                }
            }


            return stackBuilder.ToString();
        }
    }

    public class ExceptionHandlingEventArgs : System.EventArgs
    {
        public string Message { get; set; }

        public Exception ExceptionObject { get; set; }
    }
}
